﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Web;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Security.Certificates;
using Org.BouncyCastle.Tsp;
using Org.BouncyCastle.X509;
using EntGuayasSLA.BL.Archivos.Io;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.hyphenation;
using iTextSharp.text.pdf.security;
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;

namespace EntGuayasSLA.BL.Archivos.Pdf
{
    public static class PdfSigner
    {
        /// <summary>
        /// @return Si se se realizo la firma
        /// </summary>
        /// <param name="sourceDocument">Pdf origen</param>
        /// <param name="OWNER">Clave de encriptacion de pdf</param>
        /// <param name="destinationPath">Pdf destino</param>
        /// <param name="signatureCert">Certificado</param>
        /// <param name="keyPassword"></param>
        /// <param name="reason">Razon de Firma</param>
        /// <param name="location">Localidad de Firma</param>
        /// <param name="contact">Contacto de Firma</param>
        /// <param name="pthImg">Img logo firma</param>
        /// <param name="TSA_URL">Url Tsa</param>
        /// <param name="TSA_ACCNT">Usuario Tsa</param>
        /// <param name="TSA_PASSW">Pwd Tsa</param>
        public static Boolean SignPdfFile(string sourceDocument, byte[] OWNER, string destinationPath, X509Certificate2 signatureCert, string reason, string location, string contact, string pthImg, string TSA_URL, string TSA_ACCNT, string TSA_PASSW)
        {
            Boolean ifVerify = false;
            StringBuilder mError= new StringBuilder();
        
            Int32 nStamper = VerifyPdfNumSignature(sourceDocument, signatureCert, OWNER);
            if (nStamper == 0)
            {
                PdfReader reader= null;
                if (OWNER!=null)
                    using (reader = new PdfReader(sourceDocument, OWNER))
                    {
                        stamperPdf(destinationPath, signatureCert, reason, location, contact, pthImg, TSA_URL, TSA_ACCNT, TSA_PASSW, reader);
         
                    }
                else
                    using (reader = new PdfReader(sourceDocument))
                    {
                        stamperPdf(destinationPath, signatureCert, reason, location, contact, pthImg, TSA_URL, TSA_ACCNT, TSA_PASSW, reader);
         
                    }
                ifVerify = true;

            }


            return (ifVerify);
          

        }

        private static void stamperPdf(string destinationPath, X509Certificate2 signatureCert, string reason, string location, string contact, string pthImg, string TSA_URL, string TSA_ACCNT, string TSA_PASSW, PdfReader reader)
        {
            try
            {
                using (FileStream fout = new FileStream(destinationPath, FileMode.Create, FileAccess.ReadWrite))
                {

                    using (PdfStamper stamper = PdfStamper.CreateSignature(reader, fout, '\0'))
                    {

                        try
                        {
                            // appearance
                            PdfSignatureAppearance appearance = stamper.SignatureAppearance;
                            //appearance.Image = new iTextSharp.text.pdf.PdfImage();
                            appearance.Reason = reason;
                            appearance.Location = location;
                            appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(20, 10, 170, 60), 1, "Icsi-Vendor");
                            appearance.Contact = contact;
                            //
                            iTextSharp.text.Image watermark = iTextSharp.text.Image.GetInstance(pthImg);
                            appearance.Image = watermark;
                            appearance.Image.ScaleToFit(70, 70);
                            //appearance.Image.Alignment=100;
                            appearance.Image.SetAbsolutePosition(100, 100);
                            appearance.GetAppearance().AddImage(watermark);
                            appearance.SignDate = DateTime.Now;
                            appearance.Acro6Layers = true;
                            //
                            // digital signature

                            //var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(signatureCert.PrivateKey).Private;
                            //IExternalSignature es = new PrivateKeySignature(pk, "SHA-256");
                            //Org.BouncyCastle.X509.X509Certificate bcCert = DotNetUtilities.FromX509Certificate(signatureCert);
                            //var chain = new List<Org.BouncyCastle.X509.X509Certificate> { bcCert };

                            Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser();
                            Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] {
                            cp.ReadCertificate(signatureCert.RawData)};

                            IExternalSignature es = new X509Certificate2Signature(signatureCert, "SHA-1");

                            MakeSignature.SignDetached(appearance, es, chain, null, null, null, 0, CryptoStandard.CMS);
                            //
                            if (TSA_URL != "")
                                SetTimeStamp(stamper, TSA_URL, TSA_ACCNT, TSA_PASSW);
                        }
                        catch (Exception)
                        {

                        }
                        finally
                        {
                           
                            //cerrar el pdf stamper
                            stamper.Close();
                            stamper.Dispose();

                        }



                    }
                    fout.Close();
                }
              

            }
            catch (Exception)
            {
                throw;

            }
            finally
            {
                //cerrar el pdf origen
                reader.Close();
                reader.Dispose();
                
            }
        }
      
        private static void SetTimeStamp(PdfStamper stamper,string TSA_URL,string TSA_ACCNT, string TSA_PASSW)
        {
            LtvVerification v = stamper.LtvVerification;
            AcroFields af = stamper.AcroFields;
            foreach (string sigName in af.GetSignatureNames())
            {
                v.AddVerification(sigName, new OcspClientBouncyCastle(), null,
                                  LtvVerification.CertificateOption.WHOLE_CHAIN, LtvVerification.Level.OCSP_CRL,
                                  LtvVerification.CertificateInclusion.NO);
            }
            PdfSignatureAppearance sap = stamper.SignatureAppearance;
            sap.CertificationLevel = PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED;
            // Kölcsön vesszük a MicroSec-től a teszt Timestamp Authority SHA256 server-t, most éppen nem megy a NetLock-os !!!
            TSAClientBouncyCastle tsa = new TSAClientBouncyCastle(TSA_URL, TSA_ACCNT, TSA_PASSW, 6500, "sha256");
            LtvTimestamp.Timestamp(sap, tsa, null);
        }
        /// <summary>
        /// Retorna el numero de firmas asociado a un documento
        /// </summary>
        /// <param name="pdfFile"></param>
        /// <param name="objCert"></param>
        /// <param name="OWNER"></param>
        /// <returns></returns>
        public static Int32 VerifyPdfNumSignature(string pdfFile, X509Certificate2 objCert, byte[] OWNER)
        {
            
            PdfReader reader = null;
            try
            {
                reader = new PdfReader(pdfFile);
            }
            catch
            {
                reader = new PdfReader(pdfFile, OWNER);
            }
            AcroFields af = reader.AcroFields;
            var names = af.GetSignatureNames();
            reader.Close();
            return (names.Count);
        }
       
        public static Boolean VerifyPdfSignature(string pdfFile, X509Certificate2 objCert,byte[] OWNER,  out Org.BouncyCastle.X509.X509Certificate certificate, out StringBuilder sError)
        {
            Boolean ifVerify = true;
          
            sError= new StringBuilder();
            //var certificate = parser.ReadCertificate(publicKeyStream);
            //publicKeyStream.Dispose();

            //if (certificate == null)
            //{
                //--var objCert = new System.Security.Cryptography.X509Certificates.X509Certificate2(fileName, pwd, X509KeyStorageFlags.Exportable);
                certificate  = Certificate.GetX509Certificate(objCert);

            //}
            PdfReader reader = null;
            try
            {
                reader = new PdfReader(pdfFile);
            }
            catch
            {
                reader = new PdfReader(pdfFile, OWNER);
            }
            AcroFields af = reader.AcroFields;
            var names = af.GetSignatureNames();
            if (names.Count == 0)
            {
                sError.AppendLine("No Signature present in pdf file.");
                //throw new InvalidOperationException("No Signature present in pdf file.");
                ifVerify = false;
            }

            foreach (string name in names)
            {
                //
                if (!af.SignatureCoversWholeDocument(name))
                {
                     sError.AppendLine(string.Format("The signature: {0} does not covers the whole document.", name));
                    //throw new InvalidOperationException(string.Format("The signature: {0} does not covers the whole document.", name));
                    ifVerify = false;
                    break;
                }
                PdfPKCS7 pk = af.VerifySignature(name);
                var cal = pk.SignDate;
                var pkc = pk.Certificates;
                //
                var chain = new List<Org.BouncyCastle.X509.X509Certificate> { certificate };
                TimeStampToken ts = pk.TimeStampToken;


                //
                if (!pk.Verify())
                {
                    sError.AppendLine("The signature could not be verified.");
                    //throw new InvalidOperationException("The signature could not be verified.");
                    ifVerify = false;
                    break;
                }
                if (!pk.IsTsp && ts != null)
                {
                    if (!pk.VerifyTimestampImprint())
                    {
                         sError.AppendLine("The signature timestamp could not be verified.");
                        //throw new InvalidOperationException("The signature timestamp could not be verified.");
                        ifVerify = false;
                        break;
                    }


                }

                if (pk.IsTsp)
                {

                    bool tspCert = CertificateVerification.VerifyTimestampCertificates(pk.TimeStampToken, chain);

                }
                else
                {

                    //Object[] fails = CertificateVerification.VerifyCertificates(pkc, new X509Certificate[] { certificate }, null, cal);
                    var fails = CertificateVerification.VerifyCertificates(pkc, chain, null, cal);
                    if (fails == null)
                    {
                        sError.AppendLine("The file is not signed using the specified key-pair.");
                        //throw new InvalidOperationException("The file is not signed using the specified key-pair.");
                        ifVerify = false;
                        break;
                    }
                }

            }
            reader.Close();
            return (ifVerify);

        }
        
    }
}